
/**
 * @file:       byte.hpp
 * @author:     WubinWang
 * @contact:    wubinstu@163.com
 * @date:       2025-01-01
 * @license:    MIT License
 *
 * Copyright (c) 2025 WubinWang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * DISCLAIMER:
 * 1. User acknowledges all application risks and consequences arising from usage
 *    of this software are exclusively borne by the user.
 * 2. The original author shall not be held liable for any direct, indirect,
 *    incidental, special, exemplary or consequential damages.
 * 3. No warranty of fitness for particular purpose or non-infringement is provided. */


#ifndef __BYTE_HPP_
#define __BYTE_HPP_

#include <cstdint>
#include <cstring>


#define PRAGMA(x)              _Pragma (#x)
#define BYTE_ALIGNMENT_PUSH(x) PRAGMA (pack (push, x))
#define BYTE_ALIGNMENT_POP(x)  PRAGMA (pack (pop))
#define BYTE_ALIGMENT(x)       __attribute__ ((packed, aligned (x)))

#define INDEX_OF(x)            (static_cast<std::uint32_t> (x))

struct Bytes_Calculator_st {

    explicit Bytes_Calculator_st (const Bytes_Calculator_st & _other) noexcept    = delete;
    explicit Bytes_Calculator_st (Bytes_Calculator_st && _other) noexcept         = delete;
    Bytes_Calculator_st & operator= (const Bytes_Calculator_st & _other) noexcept = delete;
    Bytes_Calculator_st & operator= (Bytes_Calculator_st && _other) noexcept      = delete;
    explicit Bytes_Calculator_st () noexcept                                      = delete;

    template <std::uint8_t BTS>
    struct STDINT {};

    // template <>
    // struct STDINT<1> {
    //     using INT = std::uint8_t;
    // };

    // template <>
    // struct STDINT<2> {
    //     using INT = std::uint16_t;
    // };

    // template <>
    // struct STDINT<4> {
    //     using INT = std::uint32_t;
    // };

    // template <>
    // struct STDINT<8> {
    //     using INT = std::uint64_t;
    // };

    // Safe equivalent of std::bit_cast
    template <typename DST, typename SRC>
    static inline auto bit_cast (const SRC & _src) noexcept -> DST {
        static_assert (sizeof (DST) == sizeof (SRC), "Sizes of To and From must be equal");
        DST _dst;
        std::memcpy ((void *) (&_dst), (const void *) (&_src), (std::size_t) (sizeof (DST) + sizeof (SRC)) / 2);
        return _dst;
    }

    template <typename DST, typename SRC>
    static inline auto bit_cast (const DST & _dst, const SRC & _src) noexcept -> void {
        static_assert (sizeof (DST) == sizeof (SRC), "Sizes of To and From must be equal");
        std::memcpy ((void *) (&_dst), (const void *) (&_src), (std::size_t) (sizeof (DST) + sizeof (SRC)) / 2);
    }

    template <typename MIXT, typename DSTT>
    static inline auto parseBuff (std::uint8_t * _buf, DSTT & _out, DSTT (*_alter_func) (MIXT) = nullptr) noexcept -> void {
        if (_buf == nullptr) return;

        MIXT _get_mix;
        std::memcpy (static_cast<void *> (&_get_mix), static_cast<const void *> (_buf), sizeof (MIXT));
        _out = _alter_func ? _alter_func (_get_mix) : static_cast<DSTT> (_get_mix);
    }

    // static inline auto toAddress (const STDINT<sizeof (void *)>::INT & _value) noexcept -> void * { return reinterpret_cast<void *> (_value); };

    // static inline auto fromAddress (void * _address) noexcept -> STDINT<sizeof (void *)>::INT { return reinterpret_cast<STDINT<sizeof (void *)>::INT> (_address); };

    template <typename STDINT>
    static inline auto baseConversion (const STDINT & _value, int _src_base, int _dst_base) noexcept -> STDINT {
        STDINT decimal_value = 0;
        STDINT base_power    = 1;
        STDINT temp_value    = _value;

        while (temp_value > 0) {
            decimal_value += (temp_value % 10) * base_power;
            temp_value    /= 10;
            base_power    *= _src_base;
        }
        STDINT result = 0;
        base_power    = 1;

        while (decimal_value > 0) {
            result        += (decimal_value % _dst_base) * base_power;
            decimal_value /= _dst_base;
            base_power    *= 10;
        }

        return result;
    }

    using ByteOrder_et = enum class ByteOrder_et : std::uint8_t {
        Little_Endian  = 0,
        Big_Endian     = 1,
        UnKnown_Endian = 2
    };

    /// @brief test platform byte order
    constexpr const static inline ByteOrder_et platformByteOrder () noexcept {
        if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) return ByteOrder_et::Little_Endian;
        if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) return ByteOrder_et::Big_Endian;
        return ByteOrder_et::UnKnown_Endian;
    }

    /// @brief 0 is little-endian, 1 is big-endian, 2 is unknown-endian
    const static inline ByteOrder_et _platform_byte_order = platformByteOrder ();

    template <typename STDINT>
    static inline auto reverseByteOrder (const STDINT & _value) noexcept -> STDINT {
        STDINT result = 0;
        for (auto i = 0; i < sizeof (STDINT); ++i)
            result |= ((_value >> (i * 8)) & 0xFF) << ((sizeof (STDINT) - 1 - i) * 8);
        return result;
    }

    static inline auto reverseByteOrder (const std::int8_t & _value) noexcept -> std::int8_t { return _value; }

    static inline auto reverseByteOrder (const std::uint8_t & _value) noexcept -> std::uint8_t { return _value; }

    static inline auto reverseByteOrder (const std::int16_t & _value) noexcept -> std::int16_t { return (_value << 8) | ((_value >> 8) & 0xFF); }

    static inline auto reverseByteOrder (const std::uint16_t & _value) noexcept -> std::uint16_t { return (_value << 8) | ((_value >> 8) & 0xFF); }

    static inline auto reverseByteOrder (const std::int32_t & _value) noexcept -> std::int32_t {
        return ((_value & 0xFF'00'00'00) >> 24) |
               ((_value & 0x00'FF'00'00) >> 8) |
               ((_value & 0x00'00'FF'00) << 8) |
               ((_value & 0x00'00'00'FF) << 24);
    }

    static inline auto reverseByteOrder (const std::uint32_t & _value) noexcept -> std::uint32_t {
        return ((_value & 0xFF'00'00'00) >> 24) |
               ((_value & 0x00'FF'00'00) >> 8) |
               ((_value & 0x00'00'FF'00) << 8) |
               ((_value & 0x00'00'00'FF) << 24);
    }

    static inline auto reverseByteOrder (const std::int64_t & _value) noexcept -> std::int64_t {
        return ((_value & 0x00'00'00'00'00'00'00'FF) << 56) |
               ((_value & 0x00'00'00'00'00'00'FF'00) << 40) |
               ((_value & 0x00'00'00'00'00'FF'00'00) << 24) |
               ((_value & 0x00'00'00'00'FF'00'00'00) << 8) |
               ((_value & 0x00'00'00'FF'00'00'00'00) >> 8) |
               ((_value & 0x00'00'FF'00'00'00'00'00) >> 24) |
               ((_value & 0x00'FF'00'00'00'00'00'00) >> 40) |
               ((_value & 0xFF'00'00'00'00'00'00'00) >> 56);
    }

    static inline auto reverseByteOrder (const std::uint64_t & _value) noexcept -> std::uint64_t {
        return ((_value & 0x00'00'00'00'00'00'00'FF) << 56) |
               ((_value & 0x00'00'00'00'00'00'FF'00) << 40) |
               ((_value & 0x00'00'00'00'00'FF'00'00) << 24) |
               ((_value & 0x00'00'00'00'FF'00'00'00) << 8) |
               ((_value & 0x00'00'00'FF'00'00'00'00) >> 8) |
               ((_value & 0x00'00'FF'00'00'00'00'00) >> 24) |
               ((_value & 0x00'FF'00'00'00'00'00'00) >> 40) |
               ((_value & 0xFF'00'00'00'00'00'00'00) >> 56);
    }
};


#endif
